using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Phone.Shell;

namespace FluentAppBar
{
    internal class FluentAppBar<T> : IFluentAppBar<T>
        where T : INotifyPropertyChanged
    {
        private readonly T _viewModel;
        private readonly List<IDisposable> _buttonContainers = new List<IDisposable>();
        private int _menuItemIndex;
        private readonly List<IApplicationBarIconButton> _appBarButtons;
        private readonly List<IApplicationBarMenuItem> _appBarMenuItems;

        internal FluentAppBar(IApplicationBar applicationBar, T viewModel)
        {
            _viewModel = viewModel;
            _menuItemIndex = 0;
            _appBarButtons = applicationBar.Buttons.OfType<IApplicationBarIconButton>().ToList();
            _appBarMenuItems = applicationBar.MenuItems.OfType<IApplicationBarMenuItem>().ToList();
        }

        public IFluentAppBar<T> First(Action<IApplicationBarIconButton, T> onClick, Expression<Func<T, bool>> isEnabledProperty = null, Action<IApplicationBarIconButton, T> isEnabledChanged = null, Func<IApplicationBarIconButton, T, Uri> iconUri = null)
        {
            if (_appBarButtons.Count == 0)
            {
                throw new InvalidOperationException("First button not found");
            }
            SetupButton(_appBarButtons[0], onClick, isEnabledProperty, isEnabledChanged);
            ChangeIcon(_appBarButtons[0], iconUri);
            return this;
        }

        public IFluentAppBar<T> Second(Action<IApplicationBarIconButton, T> onClick, Expression<Func<T, bool>> isEnabledProperty = null, Action<IApplicationBarIconButton, T> isEnabledChanged = null, Func<IApplicationBarIconButton, T, Uri> iconUri = null)
        {
            if (_appBarButtons.Count < 2)
            {
                throw new InvalidOperationException("Second button not found");
            }
            SetupButton(_appBarButtons[1], onClick, isEnabledProperty, isEnabledChanged);
            ChangeIcon(_appBarButtons[1], iconUri);
            return this;
        }

        public IFluentAppBar<T> Third(Action<IApplicationBarIconButton, T> onClick, Expression<Func<T, bool>> isEnabledProperty = null, Action<IApplicationBarIconButton, T> isEnabledChanged = null, Func<IApplicationBarIconButton, T, Uri> iconUri = null)
        {
            if (_appBarButtons.Count < 3)
            {
                throw new InvalidOperationException("Third button not found");
            }
            SetupButton(_appBarButtons[2], onClick, isEnabledProperty, isEnabledChanged);
            ChangeIcon(_appBarButtons[2], iconUri);
            return this;
        }

        public IFluentAppBar<T> Fourth(Action<IApplicationBarIconButton, T> onClick, Expression<Func<T, bool>> isEnabledProperty = null, Action<IApplicationBarIconButton, T> isEnabledChanged = null, Func<IApplicationBarIconButton, T, Uri> iconUri = null)
        {
            if (_appBarButtons.Count < 4)
            {
                throw new InvalidOperationException("Fourth button not found");
            }
            SetupButton(_appBarButtons[3], onClick, isEnabledProperty, isEnabledChanged);
            ChangeIcon(_appBarButtons[3], iconUri);
            return this;
        }

        public IFluentAppBar<T> MenuItem(Action<IApplicationBarMenuItem, T> onClick, Expression<Func<T, bool>> isEnabledProperty = null, Action<IApplicationBarMenuItem, T> isEnabledChanged = null)
        {
            if (_appBarMenuItems.Count <= _menuItemIndex)
            {
                throw new InvalidOperationException(string.Format("menu item {0} not found", _menuItemIndex));
            }
            SetupButton(_appBarMenuItems[_menuItemIndex], onClick, isEnabledProperty, isEnabledChanged);
            _menuItemIndex++;
            return this;
        }

        private void SetupButton<TButton>(
            TButton btn,
            Action<TButton, T> onClick,
            Expression<Func<T, bool>> isEnabledProperty = null,
            Action<TButton, T> isEnabledChanged = null)
            where TButton : class, IApplicationBarMenuItem
        {

            var container = new SetupButtonContainer<T, TButton>(
                btn,
                _viewModel,
                onClick,
                isEnabledProperty,
                isEnabledChanged);

            _buttonContainers.Add(container);
        }

        private void ChangeIcon(IApplicationBarIconButton button, Func<IApplicationBarIconButton, T, Uri> iconUri)
        {
            if (iconUri == null)
            {
                return;
            }
            button.IconUri = iconUri(button, _viewModel);
        }

        public void Dispose()
        {
            foreach (var container in _buttonContainers)
            {
                container.Dispose();
            }
            _menuItemIndex = 0;
        }
    }
}